www.gusucode.com > VC++ 多声道MP3录音实时压缩保存程序-源码程序 > VC++ 多声道MP3录音实时压缩保存程序-源码程序/code/AudioPlayRec.cpp

    // AudioPlayRec.cpp : implementation file
// Download by http://www.NewXing.com

#include "stdafx.h"
#include "hwaudiorec.h"
#include "AudioPlayRec.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

BOOL CALLBACK InputLineProc ( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue )
{
	CAudioPlayRec *pAudioPlayRec = reinterpret_cast<CAudioPlayRec*>(dwUserValue);
	ASSERT ( pAudioPlayRec );
	CString csShortName = pLineInfo->szShortName;
	csShortName.MakeLower ();

	if ( csShortName.Find ( "microphone" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
	{
		pAudioPlayRec->m_uLineIndex_Microphone = pLineInfo->dwSource;
	}
	else if ( csShortName.Find ( "line in" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE )
	{
		pAudioPlayRec->m_uLineIndex_LineIn = pLineInfo->dwSource;
	}

	TRACE ( "csShortName = %s, dwComponentType = %d\n", csShortName, pLineInfo->dwComponentType );
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAudioPlayRec
CAudioPlayRec::CAudioPlayRec ()
: m_eStatus ( ENUM_STATUS_INVALID )
, m_hRecord ( NULL )
, m_hPlay ( NULL )
, m_dwQueuBufferSize ( 1024 )
, m_nDataQueueNum ( 0 )
, m_bRecording ( FALSE )
, m_eRecChannel ( ENUM_REC_CHANNEL_MONO )
, m_szAryInData ( NULL )
, m_szLeftInData ( NULL )
, m_szRightInData ( NULL )
, m_pAryHdr ( NULL )
, m_bAlwaysDrawTowChannel ( FALSE )
, m_uLineIndex_Microphone ( 0 )
, m_uLineIndex_LineIn ( 0 )
{
	memset ( &m_Format, 0, sizeof(WAVEFORMATEX) );
	ZeroMemory(&m_MMCKInfoParent,sizeof(m_MMCKInfoParent));
	ZeroMemory(&m_MMCKInfoChild,sizeof(m_MMCKInfoChild));
	memset ( m_hWaveFile, 0, sizeof(m_hWaveFile) );
	ResetMp3EncodeVar ();
	
	m_clrBK = RGB ( 0,0,0 );
	SetBkColor ( m_clrBK );
	
}

CAudioPlayRec::~CAudioPlayRec()
{
	StopAndFreeAll ();
	if ( m_brsBkGnd.GetSafeHandle() )
	{
		m_brsBkGnd.DeleteTempMap();
		m_brsBkGnd.DeleteObject();
	}
	
	if ( m_PenB.GetSafeHandle() )
		m_PenB.DeleteObject();
	if ( m_PenG.GetSafeHandle() )
		m_PenG.DeleteObject();
	if ( m_PenPartLine.GetSafeHandle() )
		m_PenPartLine.DeleteObject();
	if ( m_fntChannelText.GetSafeHandle() )
		m_fntChannelText.DeleteObject();
	if ( m_fntDeviceNameText.GetSafeHandle() )
		m_fntDeviceNameText.DeleteObject();
}


BEGIN_MESSAGE_MAP(CAudioPlayRec, CWnd)
//{{AFX_MSG_MAP(CAudioPlayRec)
ON_WM_ERASEBKGND()
ON_WM_SETCURSOR()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAudioPlayRec message handlers

void CAudioPlayRec::SetWaveFormat ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample )
{
	m_eRecChannel = eRecChannel;
	memset ( &m_Format, 0, sizeof(WAVEFORMATEX) );
	m_Format.cbSize				= 0;
	m_Format.wFormatTag			= WAVE_FORMAT_PCM;
	m_Format.wBitsPerSample		= wBitsPerSample;
	m_Format.nChannels			= ( (eRecChannel==ENUM_REC_CHANNEL_MONO) ? 1 : 2 );
	m_Format.nSamplesPerSec		= nSamplesPerSec;
	m_Format.nAvgBytesPerSec	= m_Format.nSamplesPerSec * (m_Format.wBitsPerSample/8);
	m_Format.nBlockAlign		= m_Format.nChannels * (m_Format.wBitsPerSample/8);
}

BOOL CAudioPlayRec::SetRelateParaAfterGetWaveFormat ()
{
	FreeBuffer ();
	m_wInQueu = (WORD) ( m_Format.nChannels + m_Format.wBitsPerSample/8 + m_Format.nSamplesPerSec/11025 );
	if ( !AllocateBuffer ( m_dwQueuBufferSize ) )
		return FALSE;
	return TRUE;
}

BOOL CAudioPlayRec::Create ( HWND hwndParent, LPRECT lpRect/*=NULL*/ )
{
	LPCTSTR lpszClassName = AfxRegisterWndClass(
		0,
		LoadCursor(AfxGetInstanceHandle(), IDC_ARROW),
		NULL, NULL );
	
	CRect rc ( 0,0,0,0 );
	if ( lpRect ) rc = *lpRect;
	
	if ( !CreateEx ( 0, lpszClassName, "",
		WS_CHILD | WS_TABSTOP,
		rc.left, rc.top, rc.Width(), rc.Height(),
		hwndParent, NULL, NULL) )
	{
		AfxMessageBox ( "Create window failed" );
		return FALSE;
	}
	
	if ( lpRect )
		ShowWindow ( SW_SHOW );
	else
		ShowWindow ( SW_HIDE );

	GetClientRect ( &m_rcClient );
	
	CClientDC dc(this);
	m_fntChannelText.CreatePointFont ( 100, "Impact", &dc );
	m_fntDeviceNameText.CreateFont ( 14, 0, 0, 0, 0, TRUE, TRUE, FALSE, 0, 0,
		0, 0, 0, "MS Sans Serif" );
	return TRUE;
}

BOOL CAudioPlayRec::OnEraseBkgnd(CDC* pDC) 
{
	DrawBackground ( pDC );
	return CWnd::OnEraseBkgnd(pDC);
}

void CAudioPlayRec::DrawBackground ( CDC *pDC )
{
	pDC->SetBkMode ( TRANSPARENT );
	DrawBackground ( pDC, TRUE );
	DrawBackground ( pDC, FALSE );
	// 画分隔线
	CPen *pOldPen = NULL;
	if ( m_PenPartLine.GetSafeHandle() )
		pOldPen = pDC->SelectObject ( &m_PenPartLine );
	pDC->MoveTo ( 0, m_rcClient.Height()/2 );
	pDC->LineTo ( m_rcClient.right, m_rcClient.Height()/2 );
	if ( pOldPen ) pDC->SelectObject ( pOldPen );
	// 画声卡名文字
	CFont *pOldFnt = NULL;
	CRect rcText = m_rcClient;
	rcText.DeflateRect ( 4, 4 );
	if ( m_fntDeviceNameText.GetSafeHandle() )
	{
		pOldFnt = pDC->SelectObject ( &m_fntDeviceNameText );
	}
	pDC->SetTextColor ( RGB(128,255,255) );
	pDC->DrawText ( m_csDeviceName, &rcText, DT_RIGHT | DT_BOTTOM | DT_SINGLELINE );
	if ( pOldFnt ) pDC->SelectObject ( pOldFnt );

	// 画外框
	pDC->Draw3dRect ( &m_rcClient, COLOR_FRAME, COLOR_FRAME );
}

CRect CAudioPlayRec::GetRectByChannel ( BOOL bLeftChannel )
{
	CRect rcBK = m_rcClient;
	if ( bLeftChannel )
	{
		rcBK.bottom = (m_rcClient.Height() - PARTLINE_HEIGHT) / 2;
	}
	else
	{
		rcBK.top = (m_rcClient.Height() + PARTLINE_HEIGHT) / 2;
	}
	return rcBK;
}

void CAudioPlayRec::DrawBackground ( CDC *pDC, BOOL bLeftChannel )
{
	ASSERT ( pDC );
	if ( !m_brsBkGnd.GetSafeHandle() ) return;

	// 画背景
	CString csCaption;
	CRect rcBK = GetRectByChannel ( bLeftChannel );
	if ( bLeftChannel )
	{
		csCaption = "Left";
	}
	else
	{
		csCaption = "Right";
	}
	pDC->FillRect ( &rcBK, &m_brsBkGnd );

	// 画文字
	CFont *pOldFnt = NULL;
	if ( m_fntChannelText.GetSafeHandle() )
	{
		pOldFnt = pDC->SelectObject ( &m_fntChannelText );
	}
	CRect rcText = rcBK;
	rcText.DeflateRect ( 2, 2 );
	pDC->SetTextColor ( RGB(255,255,0) );
	pDC->DrawText ( csCaption, &rcText, DT_LEFT | DT_TOP | DT_SINGLELINE );
	if ( pOldFnt ) pDC->SelectObject ( pOldFnt );
}

void CAudioPlayRec::DrawWave ( DWORD dwChannelBytes )
{
	CClientDC dc(this);

	BOOL bRecLeft = TRUE, bRecRight = TRUE;
	if ( !m_bAlwaysDrawTowChannel && m_eStatus == ENUM_STATUS_RECORDING && m_eRecChannel == ENUM_REC_CHANNEL_ALONE )
	{
		if ( !m_hWaveFile[ENUM_FILE_CHANNEL_LEFT] && !m_pFileMp3[ENUM_FILE_CHANNEL_LEFT] )
			bRecLeft = FALSE;
		if ( !m_hWaveFile[ENUM_FILE_CHANNEL_RIGHT] && !m_pFileMp3[ENUM_FILE_CHANNEL_RIGHT] )
			bRecRight = FALSE;
	}

	// 先将上次画的擦掉
	DrawBackground ( &dc );
	// 画波形图
	CPen *pOndPen = dc.SelectObject ( &m_PenG );

	if ( m_Format.wBitsPerSample == 8 )
	{
		if ( bRecLeft ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szLeftInData, TRUE );
		if ( bRecRight ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szRightInData, FALSE );
	}
	else
	{
		if ( bRecLeft ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szLeftInData, TRUE );
		if ( bRecRight ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szRightInData, FALSE );
	}

	if ( pOndPen )
		dc.SelectObject ( pOndPen );
}

void CAudioPlayRec::DrwaWaveShort ( CClientDC &dc, DWORD dwDrawBytes, SHORT *pShortData, BOOL bLeftChannel )
{
	CRect rcBK = GetRectByChannel ( bLeftChannel );
	int nCenterY = rcBK.CenterPoint().y;
	int y = nCenterY + (int) ( pShortData[0] * rcBK.Height() / 0xffff );
	dc.MoveTo ( 0, y );
	float fStep = (float)rcBK.Width() / (float)(dwDrawBytes);
	float fLineX = 0;
	for ( DWORD i=1; i<dwDrawBytes; i++ )
	{
		fLineX += fStep;
		y = nCenterY + (int) ( pShortData[i] * rcBK.Height() / 0xffff );
		dc.LineTo ( (int)fLineX, y );
	}
}

void CAudioPlayRec::DrwaWaveChar ( CClientDC &dc, DWORD dwDrawBytes, BYTE *pCharData, BOOL bLeftChannel )
{
	CRect rcBK = GetRectByChannel ( bLeftChannel );
	int y = (int) ( pCharData[0] * rcBK.Height() / 0xff );
	dc.MoveTo ( 0, y );
	float fStep = (float)rcBK.Width() / (float)(dwDrawBytes);
	float fLineX = 0;
	for ( DWORD i=1; i<dwDrawBytes; i++ )
	{
		fLineX += fStep;
		y = (int) ( pCharData[i] * rcBK.Height() / 0xff );
		dc.LineTo ( (int)fLineX, y );
	}
}

BOOL CAudioPlayRec::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	SetCursor ( ::LoadCursor ( NULL, IDC_ARROW ) );
	return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

BOOL CAudioPlayRec::Init (
		UINT uDeviceID/*=WAVE_MAPPER*/,
		DWORD dwBufferSize/*=1024*/,
		HWND hwndParent/*=NULL*/,
		LPRECT lpRect/*=NULL*/,
		FUNC_CallbackNotify Proc_CallbackNotify/*=NULL*/,
		WPARAM wParam/*=NULL*/
	)
{
	SetDeviceID ( uDeviceID );
	if ( m_uDeviceID != WAVE_MAPPER && (m_uDeviceID > GetWaveInCount() || m_uDeviceID > GetWaveOutCount()) )
	{
		AfxMessageBox ( "Error audio device" );
		return FALSE;
	}
	m_dwQueuBufferSize = dwBufferSize;
	m_Proc_CallbackNotify = Proc_CallbackNotify;
	m_wParam = wParam;

	// 创建窗口用来接收消息
	if ( !Create ( hwndParent, lpRect ) ) return FALSE;
	if ( hwndParent && lpRect )
	{
		m_PenG.CreatePen ( PS_SOLID, 0, RGB(0, 255, 0) );
		m_PenPartLine.CreatePen ( PS_SOLID, PARTLINE_HEIGHT, COLOR_FRAME );
	}
	
	m_eStatus = ENUM_STATUS_READY;
	return TRUE;
}

void CAudioPlayRec::SetDeviceID ( UINT uDeviceID )
{
	StopAndFreeAll ();
	m_uDeviceID = uDeviceID;
	m_csDeviceName = GetWaveInName ( m_uDeviceID );
	// 找录音线路的序号
	CVolumeInXXX::EnumerateInputLines ( m_uDeviceID, InputLineProc, (DWORD)this );
	
	if ( ::IsWindow ( m_hWnd ) )
	{
		CRect rcBK = GetRectByChannel ( FALSE );
		rcBK.DeflateRect ( 4, 4 );
		InvalidateRect ( &rcBK );
	}
}

BOOL CAudioPlayRec::Record ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample )
{
	// 录音设备正在录音
	if ( m_eStatus == ENUM_STATUS_RECORDING )
	{
		TRACE ( "录音设备正在录音 ...\n" );
		return TRUE;
	}

	m_bRecording = TRUE;
	MMRESULT mmReturn = 0;
	ResetMp3EncodeVar ();
	ASSERT ( (wBitsPerSample%8) == 0 );
	if ( wBitsPerSample > 16 )
		wBitsPerSample = 16;
	
	if ( m_eStatus != ENUM_STATUS_READY )
	{
		AfxMessageBox ( "Class status error" );
		return FALSE;
	}
	
	SetWaveFormat ( eRecChannel, nSamplesPerSec, wBitsPerSample );
	if ( !SetRelateParaAfterGetWaveFormat () )
	{
		return FALSE;
	}

	// open wavein device
	mmReturn = ::waveInOpen ( &m_hRecord, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveInOpen()");
		goto failed;
	}
	else
	{
		// make several input buffers and add them to the input queue
		for(int i=0; i<m_wInQueu; i++)
		{
			AddInputBufferToQueue ( i );
		}
		
		// start recording
		mmReturn = ::waveInStart ( m_hRecord );
		if ( mmReturn )
		{
			waveErrorMsg ( mmReturn, "waveInStart() failed");
			goto failed;
		}
	}
		
	m_eStatus = ENUM_STATUS_RECORDING;
	
	return TRUE;

failed:
	FreeBuffer ();
	return FALSE;
}

BOOL CAudioPlayRec::StartRecordAudioFile ( ENUM_FILE_CHANNEL eFileChannel, LPCTSTR lpszAudioFileName )
{
	ASSERT
	(
		(
			( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) &&
			( eFileChannel == ENUM_FILE_CHANNEL_LEFT || eFileChannel == ENUM_FILE_CHANNEL_RIGHT )
		)
		||
		(
			( m_eRecChannel != ENUM_REC_CHANNEL_ALONE ) &&
			( eFileChannel == ENUM_FILE_CHANNEL_COMMON )
		)
	);
	ASSERT ( lpszAudioFileName && strlen(lpszAudioFileName) > 0 );
	CString csFileName = lpszAudioFileName;
	csFileName.MakeLower ();
	// 需要保存到mp3文件中
	if ( csFileName.Find ( ".mp3" ) == csFileName.GetLength() - 4 )
	{
		if ( m_Format.wBitsPerSample == 8 )
		{
			AfxMessageBox ( "Cannot record 8 bits mp3" );
			goto failed;
		}
		
		if ( !LoadMp3DllFunc () )
		{
			goto failed;
		}

		if ( !PrepareEncodeMp3 ( csFileName, eFileChannel ) )
		{
			goto failed;
		}
	}
	else
	{
		// 创建一个wave文件
		if ( !CreateWaveFile ( csFileName, eFileChannel ) )
		{
			goto failed;
		}
	}

	return TRUE;

failed:
	FreeBuffer ();
	return FALSE;
}

void CAudioPlayRec::StopRecordAudioFile ( ENUM_FILE_CHANNEL eFileChannel, CString csStopFileType/*="mp3"*/ )
{
	csStopFileType.MakeLower ();
	if ( csStopFileType=="wav" && m_hWaveFile[eFileChannel] )
	{
		::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 );
		::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoParent[eFileChannel], 0 );
		::mmioClose ( m_hWaveFile[eFileChannel], 0 );
		m_hWaveFile[eFileChannel] = NULL;
	}

	if ( csStopFileType=="mp3" && m_ForMp3_hDLL_LameEnc )
	{
		EndEncodeMp3 ( eFileChannel );
	}
}

BOOL CAudioPlayRec::AddInputBufferToQueue ( int nIndex )
{
	ASSERT ( nIndex >= 0 && nIndex < m_wInQueu );
	ASSERT ( m_szAryInData[nIndex] );
	MMRESULT mmReturn = 0;
	
	LPWAVEHDR pHdr = m_pAryHdr[nIndex];
	ZeroMemory ( pHdr, sizeof(WAVEHDR) );
	pHdr->lpData = (char*)m_szAryInData[nIndex];
	pHdr->dwBufferLength = m_dwQueuBufferSize;
	
	// prepare it
	mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "AddInputBufferToQueue Failed");
		return FALSE;
	}
	
	// add the input buffer to the queue
	mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveInAddBuffer() failed");
		return FALSE;
	}
	
	m_nDataQueueNum ++;
	// no error
	return TRUE;
	
}

void CAudioPlayRec::waveErrorMsg ( MMRESULT result, LPCTSTR addstr )
{
	// say error message
	char errorbuffer[100];
	if ( m_bRecording )
		waveInGetErrorText ( result, errorbuffer, 100 );
	else
		waveOutGetErrorText ( result, errorbuffer, 100 );
	CString csMsg;
	csMsg.Format ( "WAVEIN:%x:%s %s", result, errorbuffer, addstr );
	AfxMessageBox ( csMsg );
}

LRESULT CAudioPlayRec::OnMM_WIM_DATA ( WPARAM wParam, LPARAM lParam )
{
	MMRESULT mmReturn = 0;
	
	LPWAVEHDR pHdr = (LPWAVEHDR) lParam;
	ASSERT ( pHdr );

	mmReturn = ::waveInUnprepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR));
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveInUnprepareHeader() failed" );
		return -1L;
	}
	
	if( m_eStatus == ENUM_STATUS_RECORDING )
	{
		// 提取单声道PCM数据
		int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded );
		// 根据需要保存的通道文件类型选择PCM数据和数据长度
		char *pRecData[ENUM_FILE_CHANNEL_NUM] = { pHdr->lpData, pHdr->lpData };
		int nRecBytes[ENUM_FILE_CHANNEL_NUM] = { pHdr->dwBytesRecorded, pHdr->dwBytesRecorded };
		if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE )
		{
			pRecData[ENUM_FILE_CHANNEL_LEFT] = m_szLeftInData;
			nRecBytes[ENUM_FILE_CHANNEL_LEFT] = nBytesPickup;
			pRecData[ENUM_FILE_CHANNEL_RIGHT] = m_szRightInData;
			nRecBytes[ENUM_FILE_CHANNEL_RIGHT] = nBytesPickup;
		}

		// 保存到wave文件中
		for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ )
		{
			if ( m_hWaveFile[eFileChannel] )
			{
				int length = ::mmioWrite ( m_hWaveFile[eFileChannel], pRecData[eFileChannel], nRecBytes[eFileChannel] );
				if ( length != nRecBytes[eFileChannel] )
				{
					Stop ();
					m_nDataQueueNum --;
					AfxMessageBox ( "Write file failed" );
					return -1L;
				}
			}
		}

		// 保存到mp3文件中
		if ( m_ForMp3_hDLL_LameEnc )
		{
			DWORD dwSamplesBytes = m_dwSamplesEncodeMp3Block * (m_Format.wBitsPerSample/8);
			for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ )
			{
				if ( m_pFileMp3[eFileChannel] )
				{
					int nRemainSize = m_ForMp3_dwWaveBufferSize - m_ForMp3_dwWaveDataBytes[eFileChannel];
					int nCopyBytes = (nRemainSize < nRecBytes[eFileChannel]) ? nRemainSize : nRecBytes[eFileChannel];
					memcpy ( m_ForMp3_pWaveBuffer[eFileChannel]+m_ForMp3_dwWaveDataBytes[eFileChannel], pRecData[eFileChannel], nCopyBytes );
					m_ForMp3_dwWaveDataBytes[eFileChannel] += nCopyBytes;
					
					// mp3压缩
					for ( ; m_ForMp3_dwWaveDataBytes[eFileChannel] > dwSamplesBytes; m_ForMp3_dwWaveDataBytes[eFileChannel] -= dwSamplesBytes )
					{
						WaveBufferMp3Encode ( (char*)m_ForMp3_pWaveBuffer[eFileChannel], (int)dwSamplesBytes, (ENUM_FILE_CHANNEL)eFileChannel );
						memmove ( m_ForMp3_pWaveBuffer[eFileChannel], m_ForMp3_pWaveBuffer[eFileChannel]+dwSamplesBytes, m_ForMp3_dwWaveDataBytes[eFileChannel]-dwSamplesBytes );
					}
					
					char *pUnCopyData = pRecData[eFileChannel] + nCopyBytes;
					int nUnCopyDataBytes = nRecBytes[eFileChannel] - nCopyBytes;
					nRemainSize = m_ForMp3_dwWaveBufferSize - m_ForMp3_dwWaveDataBytes[eFileChannel];
					nCopyBytes = (nRemainSize < nUnCopyDataBytes) ? nRemainSize : nUnCopyDataBytes;
					memcpy ( m_ForMp3_pWaveBuffer[eFileChannel]+m_ForMp3_dwWaveDataBytes[eFileChannel], pUnCopyData, nCopyBytes );
				}
			}
		}
		
		// reuse the buffer:
		// prepare it again
		mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) );
		if ( mmReturn )
		{
			waveErrorMsg ( mmReturn, "waveInPrepareHeader() failed" );
		}
		else // no error
		{
			// add the input buffer to the queue again
			mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) );
			if ( mmReturn )
			{
				waveErrorMsg ( mmReturn, "waveInAddBuffer() failed");
			}
			else
			{
				DrawWave( (DWORD)nBytesPickup );
				return 0L;  // no error
			}
		}
	}
	// 停止录音
	else
	{
		if ( m_nDataQueueNum == 1 )
		{
			StopRec ();
		}
		else
		{
			m_nDataQueueNum --;
		}
	}

	return 0L;
}

LRESULT CAudioPlayRec::OnMM_WOM_DONE(WPARAM wParam, LPARAM lParam)
{
	MMRESULT mmReturn = 0;
	
	LPWAVEHDR pHdr = (LPWAVEHDR) lParam;
	mmReturn = ::waveOutUnprepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveOutUnprepareHeader() failed" );
		return -1L;
	}
	
	m_nDataQueueNum--;
	
	if ( m_eStatus == ENUM_STATUS_PLAYING )
	{		
		
		int nSize = m_dwQueuBufferSize;
		if ( ReadSoundDataFromFile ( pHdr->lpData, nSize ) )
		{
			AddOutputBufferToQueue ( (int)pHdr->dwUser, nSize );			
			return 0L;
		}
		else
		{
			Stop();
		}
	}
	
	// we are closing the waveOut handle, 
	// all data must be deleted
	// this buffer was allocated in Start()	
	if ( m_nDataQueueNum == 0 && m_eStatus != ENUM_STATUS_PLAYING )
	{
		StopPlay ();
	}
	
	return 0L;
}

//
// 创建一个wave文件
//
BOOL CAudioPlayRec::CreateWaveFile ( LPCTSTR lpszWaveFileName, ENUM_FILE_CHANNEL eFileChannel )
{
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM );
	if ( m_hWaveFile[eFileChannel] )
		return TRUE;

	ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 );
	::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_DELETE ); 
	// check if file is already open
	if ( m_hWaveFile[eFileChannel] )
		return TRUE;
	
	WAVEFORMATEX wfx = m_Format;
	if ( m_eRecChannel==ENUM_REC_CHANNEL_ALONE )
		wfx.nChannels = 1;
	// open file
	m_hWaveFile[eFileChannel] = ::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_CREATE|MMIO_WRITE|MMIO_EXCLUSIVE|MMIO_ALLOCBUF );
	if ( m_hWaveFile[eFileChannel] == NULL ) 
	{
		AfxMessageBox ( "Open wave file failed" );
		return FALSE;
	}
	
	ZeroMemory ( &m_MMCKInfoParent[eFileChannel], sizeof(MMCKINFO) );
	m_MMCKInfoParent[eFileChannel].fccType = mmioFOURCC('W','A','V','E');
	
	MMRESULT mmResult = ::mmioCreateChunk( m_hWaveFile[eFileChannel],&m_MMCKInfoParent[eFileChannel], MMIO_CREATERIFF);
	
	ZeroMemory ( &m_MMCKInfoChild[eFileChannel], sizeof(MMCKINFO) );
	m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('f','m','t',' ');
	m_MMCKInfoChild[eFileChannel].cksize = sizeof(WAVEFORMATEX) + wfx.cbSize;
	mmResult = ::mmioCreateChunk(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0);
	mmResult = ::mmioWrite(m_hWaveFile[eFileChannel], (char*)&wfx, sizeof(WAVEFORMATEX) + wfx.cbSize); 
	mmResult = ::mmioAscend(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0);
	m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('d', 'a', 't', 'a');
	mmResult = ::mmioCreateChunk ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 );
	
	return TRUE;
	
}

void CAudioPlayRec::Stop()
{
	if ( m_eStatus != ENUM_STATUS_PLAYING && m_eStatus != ENUM_STATUS_RECORDING )
		return;

	MMRESULT mmReturn = 0;
	if ( m_eStatus == ENUM_STATUS_PLAYING )
	{
		if ( ::waveOutReset(m_hPlay) ) waveErrorMsg ( mmReturn, "waveOutReset() failed");
		SetTimer ( TIMER_EVENT_STOPPLAY, 1000, NULL );
	}
	else if ( m_eStatus == ENUM_STATUS_RECORDING )
	{
		SetTimer ( TIMER_EVENT_STOPREC, 1000, NULL );
	}
	
	Invalidate ( TRUE );
	m_eStatus = ENUM_STATUS_STOPING;
}

//
// 打开一个wave文件
//
BOOL CAudioPlayRec::OpenWaveFile(LPCTSTR lpszWaveFileName)
{
	ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 );
	// check if file is already open
	if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ) return FALSE; 
	
	m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = ::mmioOpen ( (LPTSTR)lpszWaveFileName,NULL,MMIO_READ );
	if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] == NULL ) 
	{
		AfxMessageBox ( "Open wave file failed" );
		return FALSE;
	}
	
	m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON].fccType = mmioFOURCC('W','A','V','E');
	MMRESULT mmResult = ::mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],NULL,MMIO_FINDRIFF);
	if(mmResult)
	{
		AfxMessageBox("Error descending into file");
		::mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0);
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL;
		return FALSE;
	}
	m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('f','m','t',' ');
	mmResult = mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],MMIO_FINDCHUNK);
	if(mmResult)
	{
		AfxMessageBox("Error descending in wave file");
		mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0);
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL;
		return FALSE;
	}
	
	DWORD bytesRead = mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],(LPSTR)&m_Format, m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].cksize );
	if ( bytesRead < 0 )
	{
		AfxMessageBox ( "Error reading PCM wave format record" );
		mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 );
		return FALSE;
	}
	if ( !SetRelateParaAfterGetWaveFormat () )
		return FALSE;
	
	// open output sound file
	mmResult = mmioAscend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], 0 );
	if ( mmResult )
	{
		AfxMessageBox ( "Error ascending in File" );
		mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 );
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL;
		return FALSE;
	}
	m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('d','a','t','a');
	mmResult = mmioDescend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON], MMIO_FINDCHUNK );
	if ( mmResult )
	{
		AfxMessageBox("error reading data chunk");
		mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0);
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL;
		return FALSE;
	}
	
	return TRUE;
	
}

BOOL CAudioPlayRec::Play(LPCTSTR lpszWaveFileName)
{
	m_bRecording = FALSE;
	if ( m_eStatus != ENUM_STATUS_READY )
	{
		AfxMessageBox ( "Class status error" );
		return FALSE;
	}
	
	if ( !OpenWaveFile ( lpszWaveFileName ) )
		return FALSE;
	
	MMRESULT mmReturn = 0;
	
	// open wavein device
	mmReturn = ::waveOutOpen ( &m_hPlay, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveOutOpen() failed");
		return FALSE;
	}
	else
	{
		// make several input buffers and add them to the input queue
		for(int i=0; i<m_wInQueu; i++)
		{
			int nSize = m_dwQueuBufferSize;
			if ( !ReadSoundDataFromFile ( m_szAryInData[i], nSize ) )
			{
				if ( i == 0 )
				{
					AfxMessageBox ( "Read sound data from file failed" );
					return FALSE;
				}
			}
			if ( !AddOutputBufferToQueue ( i, nSize ) )
				return FALSE;
		}
	}
	
	m_eStatus = ENUM_STATUS_PLAYING;
	return TRUE;
}

BOOL CAudioPlayRec::ReadSoundDataFromFile ( LPVOID data, int &size )
{
	ASSERT ( data && size > 0 );
	ASSERT ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] );
	return ( ( size = ::mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], (char*)data, size) ) > 0 );
}

BOOL CAudioPlayRec::AddOutputBufferToQueue ( int nIndex, int nSize )
{
	ASSERT ( nIndex >= 0 && nIndex < m_wInQueu );
	ASSERT ( m_szAryInData[nIndex] );
	
	MMRESULT mmReturn = 0;
	
	// create the header
	LPWAVEHDR pHdr = m_pAryHdr[nIndex];
	memset ( pHdr, 0, sizeof(WAVEHDR) );
	
	// new a buffer
	pHdr->lpData = (char*)m_szAryInData[nIndex];
	pHdr->dwBufferLength = m_dwQueuBufferSize;
	pHdr->dwBytesRecorded = nSize;
	pHdr->dwFlags = 0;
	pHdr->dwUser = nIndex;
	
	// prepare it
	mmReturn = ::waveOutPrepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) );
	if ( mmReturn )
	{
		waveErrorMsg ( mmReturn, "waveOutPrepareHeader() failed");
		return FALSE;
	}
	// write the buffer to output queue
	mmReturn = ::waveOutWrite ( m_hPlay, pHdr, sizeof(WAVEHDR) );
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveOutWrite() failed");
	// increment the number of waiting buffers
	m_nDataQueueNum++;

	int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded );
	DrawWave( (DWORD)nBytesPickup );
	return TRUE;
}

void CAudioPlayRec::StopRec ()
{
	if ( !m_hRecord ) return;
	if ( m_eStatus != ENUM_STATUS_RECORDING && m_eStatus != ENUM_STATUS_STOPING )
		return;

	MMRESULT mmReturn = 0;
	mmReturn = ::waveInReset ( m_hRecord );
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveInReset() failed" );	
	::Sleep ( 10 );
	for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ )
	{
		StopRecordAudioFile ( (ENUM_FILE_CHANNEL)eFileChannel, "mp3" );
		StopRecordAudioFile ( (ENUM_FILE_CHANNEL)eFileChannel, "wav" );
	}

	mmReturn = ::waveInClose ( m_hRecord );
	m_hRecord = NULL;
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveInClose() failed" );	
	FreeMp3Encode ();
}

void CAudioPlayRec::StopPlay()
{
	MMRESULT mmReturn = 0;
	if ( !m_hPlay ) return;
	if ( m_eStatus != ENUM_STATUS_PLAYING && m_eStatus != ENUM_STATUS_STOPING )
		return;
	
	waveOutPause ( m_hPlay );
	mmReturn = ::waveOutClose ( m_hPlay );
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveOutClose() failed" );
	m_hPlay = NULL;
	if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] )
	{
		::mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 );
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL;
	}
	m_eStatus = ENUM_STATUS_READY;
	
}

//
// 申请内存
//
BOOL CAudioPlayRec::AllocateBuffer ( DWORD dwBufferSize )
{
	m_dwQueuBufferSize = dwBufferSize;

	ASSERT ( m_wInQueu > 0 );
	m_szAryInData = new char*[m_wInQueu];
	m_szLeftInData = new char[m_dwQueuBufferSize];
	m_szRightInData = new char[m_dwQueuBufferSize];
	m_pAryHdr = new WAVEHDR*[m_wInQueu];
	if ( !m_szAryInData || !m_szLeftInData || !m_szRightInData || !m_pAryHdr )
	{
		::AfxThrowMemoryException ();
		return FALSE;
	}
	memset ( m_szAryInData, 0, sizeof(char*)*m_wInQueu );
	memset ( m_szLeftInData, 0, sizeof(char)*m_dwQueuBufferSize );
	memset ( m_szRightInData, 0, sizeof(char)*m_dwQueuBufferSize );
	memset ( m_pAryHdr, 0, sizeof(WAVEHDR*)*m_wInQueu );

	for ( int i=0; i<m_wInQueu; i++ )
	{
		m_szAryInData[i] = new char[m_dwQueuBufferSize];
		m_pAryHdr[i] = new WAVEHDR;
		if ( !m_szAryInData[i] || !m_pAryHdr[i] )
		{
			::AfxThrowMemoryException ();
			return FALSE;
		}
		memset ( m_szAryInData[i], 0, m_dwQueuBufferSize );
		memset ( m_pAryHdr[i], 0, sizeof(WAVEHDR) );
	}
	
	return TRUE;
}

void CAudioPlayRec::FreeBuffer ()
{
	// 录音或者播放尚未停止,不能释放内存
	for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ )
	{
		if ( m_hWaveFile[eFileChannel] || m_pFileMp3[eFileChannel] )
			return;
	}

	if ( m_szAryInData )
	{
		for ( int i=0; i<m_wInQueu; i++ )
		{
			if ( m_szAryInData[i] )
				delete[] m_szAryInData[i];
		}
		memset ( m_szAryInData, 0, sizeof(char*)*m_wInQueu );
		delete[] m_szAryInData;
		m_szAryInData = NULL;
	}

	if ( m_szLeftInData )
	{
		delete[] m_szLeftInData;
		m_szLeftInData = NULL;
	}

	if ( m_szRightInData )
	{
		delete[] m_szRightInData;
		m_szRightInData = NULL;
	}

	if ( m_pAryHdr )
	{
		for ( int i=0; i<m_wInQueu; i++ )
		{
			if ( m_pAryHdr[i] )
				delete[] m_pAryHdr[i];
		}
		memset ( m_pAryHdr, 0, sizeof(WAVEHDR*)*m_wInQueu );
		delete[] m_pAryHdr;
		m_pAryHdr = NULL;
	}
}

void CAudioPlayRec::SetBkColor(COLORREF clr)
{
	m_clrBK = clr;
	if ( m_brsBkGnd.GetSafeHandle() )
	{
		m_brsBkGnd.DeleteTempMap();
		m_brsBkGnd.DeleteObject();
	}
	m_brsBkGnd.CreateSolidBrush ( clr );
	if ( ::IsWindow ( m_hWnd ) )
		Invalidate ();

	if ( m_PenB.GetSafeHandle() )
		m_PenB.DeleteObject ();
	m_PenB.CreatePen ( PS_SOLID, 0, m_clrBK );
}

//
// 从立体声PCM数据中提取单声道数据,结果保存到 m_szLeftInData 、 m_szRightInData 中
// return : ------------------------------------------------------------------------------
//		单声道数据长度(字节)
//
int CAudioPlayRec::PickupMonoData ( WORD wBitsPerSample, char *szOrgData, int nOrgSize )
{
	if ( m_Format.nChannels == 1 )
	{
		ASSERT ( m_dwQueuBufferSize >= (DWORD)nOrgSize );
		memcpy ( m_szLeftInData, szOrgData, nOrgSize );
		memcpy ( m_szRightInData, szOrgData, nOrgSize );
		return nOrgSize;
	}

	ASSERT ( szOrgData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE));
	ASSERT ( m_szLeftInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE));
	ASSERT ( m_szRightInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE));
	DWORD dwBytesPerSample = wBitsPerSample/8;
	int nDestBytes_Left = 0, nDestBytes_Right = 0;
	for ( int i=0; i<nOrgSize; i+=2*dwBytesPerSample )
	{
		memcpy ( m_szLeftInData+nDestBytes_Left, szOrgData+i, dwBytesPerSample );
		nDestBytes_Left += dwBytesPerSample;
		memcpy ( m_szRightInData+nDestBytes_Right, szOrgData+i+dwBytesPerSample, dwBytesPerSample );
		nDestBytes_Right += dwBytesPerSample;
	}

	ASSERT ( nDestBytes_Left == nDestBytes_Right );
	return nDestBytes_Left;
}

//
// 为mp3编码装载动态库
//
BOOL CAudioPlayRec::LoadMp3DllFunc ()
{
	if ( m_ForMp3_hDLL_LameEnc ) return TRUE;
	BE_VERSION	Version			={0,};
	
	// Load lame_enc.dll library (Make sure though that you set the 
	// project/settings/debug Working Directory correctly, otherwhise the DLL can't be loaded
	
	m_ForMp3_hDLL_LameEnc = LoadLibrary ( "lame_enc.dll" );
	if( NULL == m_ForMp3_hDLL_LameEnc )
	{
		AfxMessageBox ( "Error loading lame_enc.DLL" );
		return FALSE;
	}
	
	// Get Interface functions from the DLL
	m_ForMp3_Proc_hInitStream		= (BEINITSTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEINITSTREAM);
	m_ForMp3_Proc_hEncodeChunk		= (BEENCODECHUNK) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEENCODECHUNK);
	m_ForMp3_Proc_hDeinitStream		= (BEDEINITSTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEDEINITSTREAM);
	m_ForMp3_Proc_hCloseStream		= (BECLOSESTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BECLOSESTREAM);
	m_ForMp3_Proc_hVersion			= (BEVERSION) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEVERSION);
	m_ForMp3_Proc_hWriteVBRHeader	= (BEWRITEVBRHEADER) GetProcAddress(m_ForMp3_hDLL_LameEnc,TEXT_BEWRITEVBRHEADER);
	m_ForMp3_Proc_hWriteInfoTag  = (BEWRITEINFOTAG) GetProcAddress(m_ForMp3_hDLL_LameEnc,TEXT_BEWRITEINFOTAG);
	
	// Check if all interfaces are present
	if ( !m_ForMp3_Proc_hInitStream || !m_ForMp3_Proc_hEncodeChunk || !m_ForMp3_Proc_hDeinitStream || !m_ForMp3_Proc_hCloseStream || !m_ForMp3_Proc_hVersion || !m_ForMp3_Proc_hWriteVBRHeader )
	{
		AfxMessageBox ( "Unable to get LAME interfaces" );
		return FALSE;
	}
	
	// Get the version number
	m_ForMp3_Proc_hVersion ( &Version );
	
	TRACE (
		"lame_enc.dll version %u.%02u (%u/%u/%u)\n"
		"lame_enc Engine %u.%02u\n"
		"lame_enc homepage at %s\n\n",	
		Version.byDLLMajorVersion, Version.byDLLMinorVersion,
		Version.byDay, Version.byMonth, Version.wYear,
		Version.byMajorVersion, Version.byMinorVersion,
		Version.zHomepage );
	
	return TRUE;
}

//
// 为mp3编码做准备
//
BOOL CAudioPlayRec::PrepareEncodeMp3 ( LPCTSTR lpszMp3FileName, ENUM_FILE_CHANNEL eFileChannel )
{
	ASSERT ( m_ForMp3_hDLL_LameEnc );
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM );
	if ( !m_ForMp3_hDLL_LameEnc ) return FALSE;
	if ( m_pFileMp3[eFileChannel] )		// 已经开始纪录mp3文件了
		return TRUE;

	ASSERT ( lpszMp3FileName && strlen(lpszMp3FileName) > 0 );
	m_csMp3FileName[eFileChannel] = lpszMp3FileName;
	BE_ERR err = 0;

	BE_CONFIG beConfig = {0};
	memset ( &beConfig, 0, sizeof(beConfig) );					// clear all fields
	
	// use the LAME config structure
	beConfig.dwConfig = BE_CONFIG_LAME;
	
	WAVEFORMATEX wfx = m_Format;
	if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE )
		wfx.nChannels = 1;
	// this are the default settings for testcase.wav
	int nMode = (wfx.nChannels==1)?BE_MP3_MODE_MONO:BE_MP3_MODE_JSTEREO;
	beConfig.format.LHV1.dwStructVersion	= 1;
	beConfig.format.LHV1.dwStructSize		= sizeof(beConfig);		
	beConfig.format.LHV1.dwSampleRate		= wfx.nSamplesPerSec;		// INPUT FREQUENCY
	beConfig.format.LHV1.dwReSampleRate		= 0;						// DON"T RESAMPLE
	beConfig.format.LHV1.nMode				= nMode;					// OUTPUT IN STREO
	beConfig.format.LHV1.dwBitrate			= 128;						// MINIMUM BIT RATE
	beConfig.format.LHV1.nPreset			= LQP_R3MIX;				// QUALITY PRESET SETTING
	beConfig.format.LHV1.dwMpegVersion		= MPEG1;					// MPEG VERSION (I or II)
	beConfig.format.LHV1.dwPsyModel			= 0;						// USE DEFAULT PSYCHOACOUSTIC MODEL 
	beConfig.format.LHV1.dwEmphasis			= 0;						// NO EMPHASIS TURNED ON
	beConfig.format.LHV1.bOriginal			= TRUE;						// SET ORIGINAL FLAG
	beConfig.format.LHV1.bWriteVBRHeader	= TRUE;						// Write INFO tag
	
	//	beConfig.format.LHV1.dwMaxBitrate		= 320;					// MAXIMUM BIT RATE
	//	beConfig.format.LHV1.bCRC				= TRUE;					// INSERT CRC
	//	beConfig.format.LHV1.bCopyright			= TRUE;					// SET COPYRIGHT FLAG	
	//	beConfig.format.LHV1.bPrivate			= TRUE;					// SET PRIVATE FLAG
	//	beConfig.format.LHV1.bWriteVBRHeader	= TRUE;					// YES, WRITE THE XING VBR HEADER
	//	beConfig.format.LHV1.bEnableVBR			= TRUE;					// USE VBR
	//	beConfig.format.LHV1.nVBRQuality		= 5;					// SET VBR QUALITY
	beConfig.format.LHV1.bNoRes				= TRUE;					// No Bit resorvoir
	
	// Preset Test
	//	beConfig.format.LHV1.nPreset			= LQP_PHONE;
	
	// Init the MP3 Stream
	DWORD dwMP3Buffer = 0;
	err = m_ForMp3_Proc_hInitStream ( &beConfig, &m_dwSamplesEncodeMp3Block, &dwMP3Buffer, &m_ForMp3_hStream[eFileChannel] );
	m_ForMp3_dwWaveBufferSize = m_wInQueu*m_dwQueuBufferSize;
	
	// Check result
	if ( err != BE_ERR_SUCCESSFUL )
	{
		AfxMessageBox ( "Error opening encoding stream (%lu)", err);
		return FALSE;
	}

	
	// Open MP3 file
	::DeleteFile ( lpszMp3FileName );
	m_pFileMp3[eFileChannel] = fopen ( lpszMp3FileName, "wb+" );
	if ( m_pFileMp3[eFileChannel] == NULL )
	{
		CString csMsg;
		csMsg.Format ( "Error creating file %s", lpszMp3FileName );
		AfxMessageBox ( csMsg );
		return FALSE;
	}	
	
	// Allocate buffer
	if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete[] m_ForMp3_pMP3Buffer[eFileChannel];
	m_ForMp3_pMP3Buffer[eFileChannel] = new BYTE[dwMP3Buffer];
	if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete[] m_ForMp3_pWaveBuffer[eFileChannel];
	m_ForMp3_pWaveBuffer[eFileChannel] = new BYTE[m_ForMp3_dwWaveBufferSize];
	
	// Check if Buffer are allocated properly
	if( !m_ForMp3_pMP3Buffer[eFileChannel] || !m_ForMp3_pWaveBuffer[eFileChannel] )
	{
		AfxMessageBox ( "Out of memory" );
		return FALSE;
	}

	return TRUE;
}

BOOL CAudioPlayRec::WaveBufferMp3Encode ( char *szWavData, int nWavSize, ENUM_FILE_CHANNEL eFileChannel)
{
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM );

	DWORD		dwWrite			=0;
	BE_ERR		err				=0;
	
	// Encode samples
	err = m_ForMp3_Proc_hEncodeChunk ( m_ForMp3_hStream[eFileChannel], nWavSize/sizeof(SHORT),
		(SHORT*)szWavData, m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite );
	
	// Check result
	if ( err != BE_ERR_SUCCESSFUL )
	{
		m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] );
		CString csMsg;
		csMsg.Format ( "m_ForMp3_Proc_hEncodeChunk() failed (%lu)", err );
		AfxMessageBox ( csMsg );
		return FALSE;
	}
	
	// write dwWrite bytes that are returned in tehe m_ForMp3_pMP3Buffer to disk
	if(fwrite(m_ForMp3_pMP3Buffer[eFileChannel],1,dwWrite,m_pFileMp3[eFileChannel]) != dwWrite)
	{
		AfxMessageBox ( "Output file write error" );
		return FALSE;
	}

	return TRUE;
	
}

//
// 结束mp3编码
//
void CAudioPlayRec::EndEncodeMp3 ( ENUM_FILE_CHANNEL eFileChannel )
{
	if ( !m_pFileMp3[eFileChannel] ) return;

	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM );
	DWORD		dwWrite			=0;
	BE_ERR		err				=0;

	if ( !m_ForMp3_Proc_hDeinitStream ) return;

	// Deinit the stream
	err = m_ForMp3_Proc_hDeinitStream ( m_ForMp3_hStream[eFileChannel], m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite );
	
	// Check result
	if ( err != BE_ERR_SUCCESSFUL )
	{
		
		m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] );
		CString csMsg;
		csMsg.Format ( "beExitStream failed (%lu)", err );
		AfxMessageBox ( csMsg );
		return;
	}
	
	// Are there any bytes returned from the DeInit call?
	// If so, write them to disk
	if ( dwWrite )
	{
		if( fwrite ( m_ForMp3_pMP3Buffer[eFileChannel], 1, dwWrite, m_pFileMp3[eFileChannel] ) != dwWrite )
		{
			AfxMessageBox ( "Output file write error" );
			return;
		}
	}
	
	// close the MP3 Stream
	ASSERT ( m_ForMp3_Proc_hCloseStream );
	m_ForMp3_Proc_hCloseStream( m_ForMp3_hStream[eFileChannel] );
	
	// Delete Buffer
	if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete [] m_ForMp3_pMP3Buffer[eFileChannel];
	m_ForMp3_pMP3Buffer[eFileChannel] = NULL;
	if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete [] m_ForMp3_pWaveBuffer[eFileChannel];
	m_ForMp3_pWaveBuffer[eFileChannel] = NULL;
	
	// Close output file
	fclose( m_pFileMp3[eFileChannel] );
	m_pFileMp3[eFileChannel] = NULL;
	
	if ( m_ForMp3_Proc_hWriteInfoTag )
	{
		// Write the INFO Tag
		m_ForMp3_Proc_hWriteInfoTag ( m_ForMp3_hStream[eFileChannel], m_csMp3FileName[eFileChannel] );
	}
	else
	{
		m_ForMp3_Proc_hWriteVBRHeader( m_csMp3FileName[eFileChannel] );
	}
}

//
// 释放mp3编码模块所申请的资源
//
void CAudioPlayRec::FreeMp3Encode ()
{
	if ( m_ForMp3_hDLL_LameEnc )
		::FreeLibrary ( m_ForMp3_hDLL_LameEnc );
	m_ForMp3_hDLL_LameEnc = NULL;
	ResetMp3EncodeVar ();
}

void CAudioPlayRec::ResetMp3EncodeVar()
{
	for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ )
	{
		m_ForMp3_pMP3Buffer[eFileChannel] = NULL;
		m_ForMp3_pWaveBuffer[eFileChannel] = NULL;
		m_pFileMp3[eFileChannel] = NULL;
		m_csMp3FileName[eFileChannel] = "";
		m_ForMp3_dwWaveDataBytes[eFileChannel] = 0;
		m_ForMp3_hStream[eFileChannel] = 0;
	}
	m_ForMp3_Proc_hInitStream = NULL;
    m_ForMp3_Proc_hEncodeChunk = NULL;
    m_ForMp3_Proc_hDeinitStream = NULL;
    m_ForMp3_Proc_hCloseStream = NULL;
    m_ForMp3_Proc_hVersion = NULL;
    m_ForMp3_Proc_hWriteVBRHeader = NULL;
    m_ForMp3_Proc_hWriteInfoTag = NULL;
    m_ForMp3_hDLL_LameEnc = NULL;
	m_ForMp3_dwWaveBufferSize = 0;
	m_dwSamplesEncodeMp3Block = 0;
}

BOOL CAudioPlayRec::PreTranslateMessage(MSG* pMsg) 
{
	if ( pMsg->message >= MM_WOM_OPEN && pMsg->message <= MM_MOM_DONE )
	{
		switch ( pMsg->message )
		{
		case MM_WIM_DATA:
			OnMM_WIM_DATA ( pMsg->wParam, pMsg->lParam );
			break;
		case MM_WIM_CLOSE:
			m_eStatus = ENUM_STATUS_READY;
			m_nDataQueueNum = 0;
			m_bRecording = FALSE;
			break;
		case MM_WOM_DONE:
			OnMM_WOM_DONE ( pMsg->wParam, pMsg->lParam );
			break;
		case MM_WOM_CLOSE:
			m_eStatus = ENUM_STATUS_READY;
			m_bRecording = FALSE;
			break;
		case MM_WIM_OPEN:
			break;
		case MM_WOM_OPEN:
			break;
		}
		
		if ( m_Proc_CallbackNotify )
		{
			m_Proc_CallbackNotify ( pMsg->message, m_wParam );
		}
	}

	return CWnd::PreTranslateMessage(pMsg);
}

void CAudioPlayRec::OnTimer(UINT nIDEvent) 
{
	switch ( nIDEvent )
	{
	case TIMER_EVENT_STOPREC:
		KillTimer ( nIDEvent );
		StopRec ();
		break;
	case TIMER_EVENT_STOPPLAY:
		KillTimer ( nIDEvent );
		StopPlay ();
		break;
	}
	
	CWnd::OnTimer(nIDEvent);
}

void CAudioPlayRec::StopAndFreeAll ()
{
	if ( m_hRecord )
	{
		StopRec ();
	}
	
	if ( m_hPlay )
	{
		StopPlay ();
	}

	FreeBuffer ();
}

//
// 获取系统中有多少个可以录音的声卡
//
UINT CAudioPlayRec::GetWaveInCount()
{
	return waveInGetNumDevs();
}

//
// 获取某录音声卡的名字
//
CString CAudioPlayRec::GetWaveInName(UINT uDeviceID)
{
	ASSERT ( uDeviceID < GetWaveInCount() );
	WAVEINCAPS tagCaps;
	switch ( waveInGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps)) )
	{
	case MMSYSERR_NOERROR:
		return tagCaps.szPname;
		break;
	default:
		return "";
	}
	
}

//
// 获取系统中有多少个可以放音的声卡
//
UINT CAudioPlayRec::GetWaveOutCount()
{
	return waveOutGetNumDevs();
}

//
// 获取某放音声卡的名字
//
CString CAudioPlayRec::GetWaveOutName(UINT uDeviceID)
{
	ASSERT ( uDeviceID < GetWaveOutCount() );
	WAVEOUTCAPS tagCaps;
	switch (waveOutGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps)))
	{
	case MMSYSERR_NOERROR:
		return tagCaps.szPname;
		break;
	default:
		return "";
	}
}

//
// 设置录音设备的来源和音量
//
void CAudioPlayRec::SetWaveInDevice ( BOOL bLineIn, DWORD dwVolume/*=ULONG_MAX*/ )
{
	CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID );
	VolumeInXXX.SetCurrentVolume ( ( dwVolume == ULONG_MAX ) ? VolumeInXXX.GetMaximalVolume() : dwVolume );
	VolumeInXXX.Enable ();
}

//
// 获取录音设备音量
//
DWORD CAudioPlayRec::GetWaveInVolume ( BOOL bLineIn, DWORD *pdwMaxVolume/*=NULL*/, DWORD *pdwMinVolume/*=NULL*/ )
{
	CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID );
	if ( pdwMaxVolume ) *pdwMaxVolume = VolumeInXXX.GetMaximalVolume ();
	if ( pdwMinVolume ) *pdwMinVolume = VolumeInXXX.GetMinimalVolume ();

	return VolumeInXXX.GetCurrentVolume ();
}

//
// 获取当前系统中 WAVE_MAPPER 在使用的声卡序号
//
int CAudioPlayRec::GetAvailableDeviceIndex()
{
	for ( DWORD i=0; i<GetWaveInCount(); i++ )
	{
		CVolumeInXXX VolumeInXXX ( 0, i );
		if ( VolumeInXXX.IsAvailable () )
			return i;
	}

	return -1;
}